<?php
/**
 * User: Simon
 * Date: 2025/3/17
 * Note: *
 */
namespace App\Http\Controllers\Admin;

use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Response;
use Illuminate\Support\Str;

class CurdController extends BackendController
{
    /**
     * 指定请求处理类
     * @var String
     */
    protected $classRequest;

    /**
     * 指定模型类
     * @var String
     */
    protected string $classModel = '';

    /**
     * 模型
     * @var Model|null
     */
    protected $model;

    /**
     * 可搜索字段
     * @var array
     */
    protected array $searchFields = [];

    /**
     * 排序字段
     * @var array
     */
    protected array $orderFields = [];

    /**
     * 关联数据
     * @var array
     */
    protected array $relationWiths = [];

    /**
     * 关联计数
     * @var array
     */
    protected $withCounts = [];

    /**
     * 加载的查询器
     * @var array
     */
    protected $appends = [];

    public const FIELD_TYPE_NORMAL = 'ofField';
    public const FIELD_TYPE_NUMERIC = 'ofFieldNumeric';
    public const FIELD_TYPE_MULTI_VALUE = 'ofMultiValue';
    public const FIELD_TYPE_LIKE = 'ofLike';
    public const FIELD_TYPE_LIKE_LEFT = 'ofLikeLeft';
    public const FIELD_TYPE_BIT = 'ofBit';
    public const FIELD_TYPE_DATETIME_RANGE = 'ofDatetimeRange';
    public const FIELD_TYPE_TIMESTAMP_RANGE = 'ofTimestampRange';
    public const FIELD_TYPE_JSON_CONTAINS_NUMERIC = 'ofJsonContainsNumeric';
    public const FIELD_TYPE_JSON_CONTAINS_NUMERIC_ANY = 'ofJsonContainsNumericAny';
    public const FIELD_TYPE_IP_LONG = 'ofIp2long';

    public function __construct(Request $request)
    {
        parent::__construct($request);
        // $this->request = $this->classRequest ? (new $this->classRequest)->capture() : $request;
        $this->model = $this->classModel ? new $this->classModel : null;
    }

    /**
     * @return Request
     */
    public function getRequest(): Request
    {
        return $this->classRequest ? (new $this->classRequest)->capture() : request();
    }

    /**
     * 指定模型, 或 根据路由获取模型
     * @param Request $request
     * @return Model
     * @throws \Exception
     */
    public function getModel(Request $request): Model
    {
        if ($this->model) return $this->model;
        if ($this->classModel) {
            $this->model = new $this->classModel();
            return $this->model;
        }

        $routeName = $request->route()->getName();
        $modelName = substr($routeName, 0, strrpos($routeName, '.'));
        $modelName = Str::studly($modelName);
        $class = "App\Models\\{$modelName}";

        // 查找模型
        if (!class_exists($class)) $class = "App\Models\Game\\{$modelName}";
        if (!class_exists($class)) throw new \Exception("请检查模型{$modelName}是否存在");

        $this->model = new $class();
        return $this->model;
    }

    /**
     * @param Request $request
     * @return Application|ResponseFactory|Response|void
     */
    public function indexBefore(Request $request){}

    public function index(Request $request)
    {
        // 列表可能要做一些预处理
        if (method_exists($this, "indexBefore")) {
            $this->indexBefore($request);
        }
        $model = $this->getModel($request);
        $keyName = $model->getKeyName();
        // 指定搜索
        if ($this->searchFields) {
            foreach ($this->searchFields as $field => $type) {
                // 未指定处理方法时, 默认 normal
                if (is_numeric($field)) {
                    $field = $type;
                    $type = self::FIELD_TYPE_NORMAL;
                }

                $model = $model->$type($field, $request->$field);
            }
        }
        // 指定排序
        if (empty($this->orderFields) && $request->sortField && $request->sortValue) {
            $this->orderFields[$request->sortField] = $request->sortValue;
        }
        if ($this->orderFields) {
            foreach ($this->orderFields as $field => $type) {
                $model = $model->orderBy($field, $type);
            }
        } else {
            $model = $model->orderByDesc($keyName);
        }
        // 指定关联查询
        if ($this->relationWiths) {
            foreach ($this->relationWiths as $relation) {
                $model = $model->with($relation);
            }
        }
        // 关联模型计数
        if ($this->withCounts) {
            foreach ($this->withCounts as $withCount) {
                $model = $model->withCount($withCount);
            }
        }
        $lists = $model->paginate($request->pageSize);

        if ($this->appends) {
            $appends = $this->appends;
            $lists->each(function ($item) use ($appends) {
                $item->append($appends);
            });
        }

        return JsonResource::collection($lists);
    }

    /**
     * @param Request $request
     * @return Application|ResponseFactory|Response|void
     */
    public  function storeBefore(Request $request){}

    /**
     * 创建
     * @param Request $request
     * @return Application|ResponseFactory|Response
     * @throws \Exception
     */
    public function store(Request $request)
    {
        if (method_exists($this, 'storeBefore')
            && ($response = $this->storeBefore($request))
            && $response instanceof Response) return $response;

        if (!$model = $this->getModel($request)->create($request->all())) return responseException(__('web.failed'));

        // 整合在控制器里, 减少observe数量
        if (method_exists($this, 'storeAfter')) $this->storeAfter($model, $request);

        return responseMessage(__('web.success'));
    }

    /**
     * @param Request $request
     * @return Application|ResponseFactory|Response|void
     */
    public  function updateBefore(Request $request){}

    /**
     * 更新
     * @param Request $request
     * @param $primaryKey
     * @return Application|ResponseFactory|Response
     * @throws \Exception
     */
    public function update(Request $request, $primaryKey)
    {
        // put 方法也会进来, 这边转发
        if (!is_numeric($primaryKey)) {
            $method = Str::camel($primaryKey);// batch-update => batchUpdate
            if (method_exists($this, $method)) return $this->$method();
            abort(404);
        }

        if (method_exists($this, 'updateBefore')
            && ($response = $this->updateBefore($request))
            && $response instanceof Response) return $response;

        $model = $this->getModel($request)->find($primaryKey);

        if (!$model->update($request->all())) return responseException(__('web.failed'));

        // 整合在控制器里, 减少observe数量
        if (method_exists($this, 'updateAfter')) $this->updateAfter($model, $request);

        return responseMessage(__('web.success'));
    }

    /**
     * 详情
     * @param Request $request
     * @param $primaryKey
     * @return JsonResource
     * @throws \Exception
     */
    public function show(Request $request, $primaryKey)
    {
        $model = $this->getModel($request);
        // 指定关联查询
        if ($this->relationWiths) {
            foreach ($this->relationWiths as $relation) {
                $model = $model->with($relation);
            }
        }
        $model = $model->findOrFail($primaryKey);
        return new JsonResource($model);
    }

    /**
     * @param Model|Collection $model
     * @return Application|ResponseFactory|Response|void
     */
    public function destroyBefore(Model|Collection $model){}

    /**
     * 删除
     * @param Request $request
     * @param $primaryKey
     * @return Application|ResponseFactory|Response
     * @throws \Exception
     */
    public function destroy(Request $request, $primaryKey)
    {
        if (!$primaryKey || !is_numeric($primaryKey)) {
            return $this->destroyMultiple($request, $request->ids);
        }

        $model = $this->getModel($request)->find($primaryKey);
        if (method_exists($this, 'destroyBefore')
            && ($response = $this->destroyBefore($model))
            && $response instanceof Response) return $response;

        if (!$model->delete()) return responseException(__('web.failed'));

        if (method_exists($this, 'destroyAfter')) $this->destroyAfter($model);

        return responseMessage(__('web.success'));
    }

    /**
     * 批量删除
     * @param Request $request
     * @param array $ids
     * @return Response|Application|ResponseFactory
     * @throws \Exception
     */
    public function destroyMultiple(Request $request, array $ids)
    {
        $model =  $this->getModel($request);
        $keyName = $model->getKeyName();
        $query = $model->whereIn($keyName, $ids);
        $collection = $query->get();

        if (method_exists($this, 'destroyBefore')
            && ($response = $this->destroyBefore($collection))
            && $response instanceof Response) return $response;

        if (!$query->delete()) return responseException(__('web.failed'));

        if (method_exists($this, 'destroyAfter')) $this->destroyAfter($collection);

        return responseMessage(__('web.success'));
    }

    /**
     * 不做前置校验
     * @param Request $request
     * @param $primaryKey
     * @return Application|ResponseFactory|Response
     * @throws \Exception
     */
    public function easyUpdate(Request $request, $primaryKey)
    {
        $model = $this->getModel($request)->find($primaryKey);

        if (!$model->update($request->all())) return responseException(__('web.failed'));

        // 整合在控制器里, 减少observe数量
        if (method_exists($this, 'updateAfter')) $this->updateAfter($model, $request);

        return responseMessage(__('web.success'));
    }
}
